﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;

namespace DataAccess
{
    public class DataAccessDAO
    {

        
        /// <summary>
        /// Constructor privado, para evitar instancias de la clase
        /// </summary>
        private DataAccessDAO()
        {
            //Se aplica patron singleton, con un solo DAO puede realizar todas sus operaciones
        }

        private static DataAccessDAO dataAccessDao = new DataAccessDAO();

        /// <summary>
        /// Metodo que devuelve la unica instancia del DAO
        /// </summary>
        /// <returns>Objeto DataAcessDAO</returns>
        public static DataAccessDAO GetInstance()
        {
            return dataAccessDao;
            //Se crearan instancias para no congelar la aplicacion con multiples instancias
            //return new DataAccessDAO();
        }

        public List<T> GetTableList<T>()
        {
            List<T> listaRetorno = new List<T>();
            DataAccessCommand comm = new DataAccessCommand(DataAccessHelper.CreateSelectTable<T>(), CommandType.Text);

            listaRetorno = GetList<T>(comm);

            return listaRetorno;

        }

        public int ExecuteAction(DataAccessCommand comm)
        {
            return comm.Command.ExecuteNonQuery();
        }

        /// <summary>
        /// Metodo que devuelve solo el primer objeto encontrado en una lista
        /// Normalmente se debera usar en consulta que regresen una fila (Mas sera exceso de memoria y datos)
        /// </summary>
        /// <typeparam name="T">Objeto Result que se espera de regreso</typeparam>
        /// <param name="comm">Commando que tendra parametros SQL, nombre de consulta, tipo de conexion, etc.</param>
        /// <returns>Objeto Result esperado</returns>
        /// <seealso cref="GetList<T>"/>
        public T GetOneRow<T>(DataAccessCommand comm)
        {
            return GetList<T>(comm).First();
        }

        /// <summary>
        /// Metodo que devolvera el resultado en un objeto DataSet
        /// </summary>
        /// <param name="comm"></param>
        /// <returns></returns>
        public DataSet GetDataSet(DataAccessCommand comm)
        {
            DataSet dataSet = new DataSet();
            DataAdapter adapter = comm.CreateDataAdapter();
            adapter.Fill(dataSet);

            return dataSet;
        }

        public object GetFirstResult(DataAccessCommand comm)
        {
            return comm.Command.ExecuteScalar();
        }

        /// <summary>
        /// Metodo que devolvera una lista de datos de acuerdo a un objeto de dominio (Clase que Mapea Campos Esperados)
        /// cabe observar que se deberan cumplir una serie de criterios en el objeto, debera tener el constructor sin parametros disponible,
        /// ademas debera contener en sus propiedades el atributo NameColumn de la clase DataAccessPropierties
        /// </summary>
        /// <typeparam name="T">Objeto de Dominio</typeparam>
        /// <param name="comm">Comando a Ejecutar en Base de Datos</param>
        /// <returns>Lista con los Objetos de Dominio</returns>
        public List<T> GetList<T>(DataAccessCommand comm)
        {
            List<T> listaRetorno = new List<T>();
            IDbCommand command = comm.Command;

            try
            {
                    IDataReader reader = command.ExecuteReader();

                    using (reader)
                    {
                        while (reader.Read())
                        {
                            //Se crea el objeto con un casteo
                            T objectDomain = (T)DataAccessHelper.CreateDomainObject<T>(reader);

                            //Se agrega a la lista
                            listaRetorno.Add(objectDomain);
                        }
                    }
            }
            catch (DataAccessException ex)
            {
                throw ex;
            }
            catch (SqlException ex)
            {
                throw ex;
            }
            catch (Exception ex)
            {
                throw ex;
            }
            
            return listaRetorno;
        }
    }
}
